package io.gitee.tziye.core.kv.impl;

import io.gitee.tziye.common.RenaiUtil;
import io.gitee.tziye.core.instance.DefaultRenaiClient;
import io.gitee.tziye.core.kv.ListOperator;
import io.gitee.tziye.core.kv.RenaiKey;
import io.gitee.tziye.core.kv.RenaiValue;
import io.gitee.tziye.enums.KvType;
import io.gitee.tziye.jdbc.JdbcProxy;
import io.gitee.tziye.jdbc.ProcedureResult;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static io.gitee.tziye.common.Constants.*;

public class DefaultListOperator extends ListOperator {

    private final KvCommonOperator kvCommonOperator;
    private final JdbcProxy jdbcProxy;

    public DefaultListOperator(DefaultRenaiClient renaiClient) {
        super(renaiClient);
        kvCommonOperator = new KvCommonOperator(renaiClient);
        jdbcProxy = renaiClient.getJdbcProxy();
    }

    @Override
    public void saveKey(String key, Integer expireSeconds) {
        kvCommonOperator.saveKey(key, expireSeconds, KvType.List);
    }

    @Override
    public RenaiKey key(String key) {
        return kvCommonOperator.key(key, KvType.List);
    }

    @Override
    public List<RenaiKey> keys(List<String> keys) {
        return kvCommonOperator.keys(keys, KvType.List);
    }

    @Override
    public List<RenaiKey> keys() {
        return kvCommonOperator.keys(KvType.List);
    }

    @Override
    public List<RenaiKey> like(String pattern) {
        return kvCommonOperator.like(pattern, KvType.List);
    }

    @Override
    public List<RenaiKey> regexp(String pattern) {
        return kvCommonOperator.regexp(pattern, KvType.List);
    }

    @Override
    public List<RenaiValue> values(String key) {
        return kvCommonOperator.values(key, KvType.List);
    }

    @Override
    public int total() {
        return kvCommonOperator.total(KvType.List);
    }

    @Override
    public int size(String key) {
        return kvCommonOperator.size(key, KvType.List);
    }

    @Override
    public void expire(String key, int expireSeconds) {
        kvCommonOperator.expire(key, expireSeconds, KvType.List);
    }

    @Override
    public boolean exists(String key) {
        return kvCommonOperator.exists(key, KvType.List);
    }

    @Override
    public <T> boolean contains(String key, T value) {
        return kvCommonOperator.contains(key, value, KvType.List);
    }

    @Override
    public int clear(String key) {
        return kvCommonOperator.clear(key, KvType.List);
    }

    @Override
    public <T> boolean remove(String key, T value) {
        return kvCommonOperator.remove(key, value, KvType.List);
    }

    @Override
    public <T> int removeValues(String key, Collection<T> values) {
        return kvCommonOperator.removeValues(key, values, KvType.List);
    }

    @Override
    public boolean delete(String key) {
        return kvCommonOperator.delete(key, KvType.List);
    }

    @Override
    public int delete(Collection<String> keys) {
        return kvCommonOperator.delete(keys, KvType.List);
    }

    @Override
    public void flush() {
        kvCommonOperator.flush(KvType.List);
    }

    @Override
    public <T> List<T> members(String key, Class<T> clazz) {
        assert clazz != null;
        List<RenaiValue> renaiValues = kvCommonOperator.values(key, KvType.List);
        return renaiValues.stream().map(rv -> RenaiUtil.parse(rv.getV(), clazz)).toList();
    }

    @Override
    public <T> void add(String key, T value) {
        commonInsert(key, null, List.of(value), 1);
    }

    @Override
    public <T> void add(String key, Collection<T> values) {
        commonInsert(key, null, values, 1);
    }

    @Override
    public <T> void addIfAbsent(String key, T value) {
        commonInsert(key, null, List.of(value), 2);
    }

    @Override
    public <T> void addIfAbsent(String key, Collection<T> values) {
        commonInsert(key, null, values, 2);
    }

    @Override
    public <T> void insert(String key, int idx, T value) {
        commonInsert(key, idx, List.of(value), 1);
    }

    @Override
    public <T> void insert(String key, int idx, Collection<T> values) {
        commonInsert(key, idx, values, 1);
    }

    private <T> void commonInsert(String key, Integer idx, Collection<T> values, int flag) {
        assert StringUtils.isNotBlank(key) && CollectionUtils.isNotEmpty(values)
                && values.stream().noneMatch(Objects::isNull);
        Map<String, Object> params = procedureParams(key);
        params.put(IDX, idx);
        params.put(VALUE, RenaiUtil.string(values));
        params.put(FLAG, flag);
        jdbcProxy.call(KV_LIST_INSERT, params);
    }

    private <T> List<T> commonGet(String key, int start, int count, Class<T> clazz, boolean rev) {
        assert clazz != null;
        ProcedureResult result = commonRange(key, start, count, rev, 1);
        List<RenaiValue> values = result.list(RenaiValue.class);
        return values.stream().map(v -> RenaiUtil.parse(v.getV(), clazz)).toList();
    }

    @Override
    public <T> T get(String key, int idx, Class<T> clazz) {
        List<T> list = commonGet(key, idx, 1, clazz, false);
        return CollectionUtils.isEmpty(list) ? null : list.get(0);
    }

    @Override
    public <T> List<T> get(String key, int start, int count, Class<T> clazz) {
        return commonGet(key, start, count, clazz, false);
    }

    @Override
    public <T> T revGet(String key, int idx, Class<T> clazz) {
        List<T> list = commonGet(key, idx, 1, clazz, true);
        return CollectionUtils.isEmpty(list) ? null : list.get(0);
    }

    @Override
    public <T> List<T> revGet(String key, int start, int count, Class<T> clazz) {
        return commonGet(key, start, count, clazz, true);
    }

    private <T> List<T> commonPop(String key, int start, int count, Class<T> clazz, boolean rev) {
        assert clazz != null;
        ProcedureResult result = commonRange(key, start, count, rev, 3);
        List<RenaiValue> values = result.list(RenaiValue.class);
        return values.stream().map(v -> RenaiUtil.parse(v.getV(), clazz)).toList();
    }

    @Override
    public <T> T pop(String key, int idx, Class<T> clazz) {
        List<T> list = commonPop(key, idx, 1, clazz, false);
        return CollectionUtils.isEmpty(list) ? null : list.get(0);
    }

    @Override
    public <T> List<T> pop(String key, int idx, int count, Class<T> clazz) {
        return commonPop(key, idx, count, clazz, false);
    }

    @Override
    public <T> T revPop(String key, int idx, Class<T> clazz) {
        List<T> list = commonPop(key, idx, 1, clazz, true);
        return CollectionUtils.isEmpty(list) ? null : list.get(0);
    }

    @Override
    public <T> List<T> revPop(String key, int idx, int count, Class<T> clazz) {
        return commonPop(key, idx, count, clazz, true);
    }

    private int commonDelete(String key, int start, int count, boolean rev) {
        ProcedureResult result = commonRange(key, start, count, rev, 2);
        return result.updated();
    }

    @Override
    public int delete(String key, int start, int count) {
        return commonDelete(key, start, count, false);
    }

    @Override
    public int revDelete(String key, int start, int count) {
        return commonDelete(key, start, count, true);
    }

    private ProcedureResult commonRange(String key, int start, int count, boolean rev, int flag) {
        assert StringUtils.isNotBlank(key) && start >= 0 && count > 0;
        Map<String, Object> params = procedureParams(key);
        params.put(START, start);
        params.put(SIZE, count);
        params.put(REV, rev ? 1 : -1);
        params.put(FLAG, flag);
        return jdbcProxy.call(KV_LIST_RANGE, params);
    }

    @Override
    public <T> int replace(String key, T oldValue, T newValue) {
        assert StringUtils.isNotBlank(key) && oldValue != null && newValue != null;
        String sql = "UPDATE renai_kv_list_value_valid SET v = ? WHERE k =? AND v = ?";
        return jdbcProxy.update(sql, RenaiUtil.string(newValue), key, RenaiUtil.string(oldValue));
    }

    @Override
    public <T> List<Integer> indexOf(String key, T value) {
        assert StringUtils.isNotBlank(key) && value != null;
        String sql = "SELECT score FROM renai_kv_list_value_valid WHERE k = ? AND v = ? ORDER BY score";
        return jdbcProxy.queryForList(sql, Integer.class, key, RenaiUtil.string(value));
    }

    @Override
    public <T> Integer firstIndexOf(String key, T value) {
        List<Integer> idx = indexOf(key, value);
        return CollectionUtils.isEmpty(idx) ? null : idx.get(0);
    }

    @Override
    public <T> Integer lastIndexOf(String key, T value) {
        List<Integer> idx = indexOf(key, value);
        return CollectionUtils.isEmpty(idx) ? null : idx.get(idx.size() - 1);
    }

}
